//
//  SSDefault.swift
//  SplendidSwift
//
//  Created by WY on 2020/11/14.
//


import Foundation

@propertyWrapper
public struct SSDefault<T: PropertyListValue> {
    public let key: UserDefaultsKey

    public var wrappedValue: T? {
        get { UserDefaults.standard.value(forKey: key.rawValue) as? T }
        set { UserDefaults.standard.set(newValue, forKey: key.rawValue) }
    }

    public var projectedValue: SSDefault<T> { return self }
    
    public func observe(change: @escaping (T?, T?) -> Void) -> DefaultsObservation {
        return DefaultsObservation(key: key) { old, new in
            change(old as? T, new as? T)
        }
    }
    
    public init(key: UserDefaultsKey) {
        self.key = key
    }
}


public protocol PropertyListValue {}

extension Data: PropertyListValue {}
extension String: PropertyListValue {}
extension Date: PropertyListValue {}
extension Bool: PropertyListValue {}
extension Int: PropertyListValue {}
extension Double: PropertyListValue {}
extension Float: PropertyListValue {}
extension CGFloat: PropertyListValue {}
extension Array: PropertyListValue where Element: PropertyListValue {}
extension Dictionary: PropertyListValue where Key == String, Value: PropertyListValue {}

public struct UserDefaultsKey: RawRepresentable {
    public let rawValue: String
    public init(rawValue: String) {
        self.rawValue = rawValue
    }
}

extension UserDefaultsKey: ExpressibleByStringLiteral {
    public init(stringLiteral: String) {
        rawValue = stringLiteral
    }
}

public class DefaultsObservation: NSObject {
    public let key: UserDefaultsKey
    public var onChange: (Any, Any) -> Void

    public init(key: UserDefaultsKey, onChange: @escaping (Any, Any) -> Void) {
        self.onChange = onChange
        self.key = key
        super.init()
        UserDefaults.standard.addObserver(self, forKeyPath: key.rawValue, options: [.old, .new], context: nil)
    }

    public override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey: Any]?, context: UnsafeMutableRawPointer?) {
        guard let change = change, object != nil else { return }
        onChange(change[.oldKey] as Any, change[.newKey] as Any)
    }

    deinit {
        UserDefaults.standard.removeObserver(self, forKeyPath: key.rawValue, context: nil)
    }
}



